[TOC]
排序
冒泡排序
冒泡排序思想:将数组中的当前项和后一项进行比较,如果当前项比后一项大,则两者交换顺序。
const arr = [5, 4, 3, 2, 1];
/**
* 外循环控制比较的轮数
* 内循环控制每轮比较的次数
*/
function bubble(arr) {
// 轮数(需要arr.length - 1轮)
// 这里数组中一共有5个数,只需要把数组中最大的4个数依次放到数组末尾即可。
for (let i = 0; i < arr.length - 1; i++) {
// 每轮比较的次数
// 减去1的原因:每一轮都不需要和自己比
// 减去i的原因:比如说第二轮的话,i等于1,因为在第一轮的时候已经把数组中最大的元素放到了数组末尾,因此不需要再跟最后一个元素比较,因此需要减去i。以此类推,第三轮和第四轮也是同理。
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// let temp = arr[j];
// arr[j] = arr[j+1];
// arr[j+1] = temp;
[arr[j], arr[j+1]] = [arr[j+1], arr[j]];
}
}
}
return arr;
}
console.log(bubble(arr)); // [1, 2, 3, 4, 5]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
插入排序
构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入 实现过程:
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已排序的元素序列中从后向前扫描;
- 如果该元素(以排序)大于新元素,将元素向后移一位
- 在取出一个元素,比较之前的,直到找到自己合适的位置
const arr = [12, 6, 23, 4, 36, 18];
function insert(arr) {
const res = []; // 结果数组
res.push(arr[0]); // 默认将第一项先放到结果数组中
// 从第二项开始依次插入
for (let i = 1; i < arr.length; i++) {
// currentEle是当前要插入的元素
const currentEle = arr[i];
// 从后往前比
for (let j = res.length - 1; j >= 0; j--) {
if (currentEle > res[j]) {
// 如果currentEle比res[j]大,则将currentEle插入到res[j]的前面
// 所以这里splice方法的第一个参数是j+1,而不是j
res.splice(j + 1, 0, currentEle);
break;
}
// 上面的if语句会一直从后向前比较,直到和res中的第一项比较完
if (j === 0) {
// 走到这里说明,currentEle比res中的第一项还要小,直接插入到数组最前面即可
res.unshift(currentEle);
}
}
}
return res;
}
console.log(insert(arr));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
快速排序
快速排序:核心思想是递归。使用分治法把一个串(list)分为两个子串(sub-lists)。
实现过程:
- 从数组中挑出一个元素,成为一个基准;
- 重新排列数组,所有元素比基准小的摆在基准前面,所有元素比基准大的摆在基准后面(相同的可以摆在一边)这个分区退出之后,该基准就处于数列的中间位置。成为分区操作。
- 递归的把小于基准值的子数列和大于基准值元素的子数列排序
const arr = [12, 6, 23, 4, 36, 18];
function quick(arr) {
// 第四步:结束递归(当arr中小于等于一项,则不用继续递归处理)
if (arr.length <= 1) {
return arr;
}
// 第一步:找到数组的中间项,并在原数组中将其移除
const middleIndex = Math.floor(arr.length / 2); // 向下取整
const [middleValue] = arr.splice(middleIndex, 1);
// 第二步:准备左右两个数组,循环数组中剩余的项,比中间项小的放到左数组中,反之放到右数组中
const leftArr = [];
const rightArr = [];
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
item < middleValue ? leftArr.push(item) : rightArr.push(item);
}
// 第三步:采用递归让左右两边的数组继续这样处理,直到左右两边都排好序
return quick(leftArr).concat(middleValue, quick(rightArr));
}
console.log(quick(arr));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
方法2:直接将数组第一项作为中间项。
const arr = [12, 6, 23, 4, 36, 18, 16];
function quick(arr) {
// 第四步:结束递归(当arr中小于等于一项,则不用继续递归处理)
if (arr.length <= 1) {
return arr;
}
// 第一步:直接将数组第一项作为中间项
// 第二步:准备左右两个数组,循环数组中剩余的项,比中间项小的放到左数组中,反之放到右数组中
const leftArr = [];
const rightArr = [];
const firstValue = arr[0];
// 从第二项开始循环处理。
for (let i = 1; i < arr.length; i++) {
const item = arr[i];
item < firstValue ? leftArr.push(item) : rightArr.push(item);
}
// 第三步:采用递归让左右两边的数组继续这样处理,直到左右两边都排好序
return quick(leftArr).concat(firstValue, quick(rightArr));
}
console.log(quick(arr));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
选择排序
/*
寻找第i小的数的位置,放到i位置上
*/
const arr = [12, 6, 23, 4, 36, 18];
function selectSort(arr) {
const len = arr.length;
for (let i = 0; i < len; i++) {
let minIndex = i;
for (let j = i + 1; j < len; j++) {
minIndex = arr[j] <= arr[minIndex] ? j : minIndex;
}
if (minIndex !== i) {
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
}
return arr;
}
console.log(selectSort(arr));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
归并排序
function MergeSort (arr, low, high) {
const length = arr.length
if (low === high) {
return arr[low]
}
const mid = Math.floor((low + high)/2)
MergeSort(arr, low, mid)
MergeSort(arr, mid + 1, high)
merge(arr, low, high)
return arr
}
function merge (arr, low, high) {
const mid = Math.floor((low + high)/2)
let left = low
let right = mid + 1
const result = []
while (left <= mid && right <= high) {
if (arr[left] <= arr[right]) {
result.push(arr[left++])
} else {
result.push(arr[right++])
}
}
while (left <= mid) {
result.push(arr[left++])
}
while (right <= high) {
result.push(arr[right++])
}
arr.splice(low, high-low+1, ...result)
}
const test = [2, 34, 452,3,5, 785, 32, 345, 567, 322,5]
console.log(MergeSort(test, 0, test.length - 1))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
希尔排序
插入排序的改进版。对间隔 gap 为一组的数进行插入排序
function ShellSort (arr) {
const length = arr.length
let gap = Math.floor(length)
while (gap) {
for (let i = gap; i < length; i++) {
const temp = arr[i]
let j
for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap) {
arr[j + gap] = arr[j]
}
arr[j + gap] = temp
}
gap = Math.floor(gap / 2)
}
return arr
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
堆排序
function HeapSort (arr) {
const length = arr.length
// 调整初始堆,调整完其实也确定了最大值
// 但此时最大值是在 arr[0] 中
for (let i = Math.floor(length/2) - 1; i >= 0; i--) {
adjustHeap(arr, i, length)
}
// 把 arr[0](最大值)换到后面
for (let i = length - 1; i >=0; i--) {
const temp = arr[0]
arr[0] = arr[i]
arr[i] = temp
adjustHeap(arr, 0, i)
}
return arr
}
// size 是还需要调整的堆的大小
// 随着一个个最大值的确定,size 会越来越小
function adjustHeap (arr, position, size) {
const left = position * 2 + 1
const right = left + 1
let maxIndex = position
if (left < size && arr[left] > arr[maxIndex]) {
maxIndex = left
}
if (right < size && arr[right] > arr[maxIndex]) {
maxIndex = right
}
if (maxIndex !== position) {
const temp = arr[position]
arr[position] = arr[maxIndex]
arr[maxIndex] = temp
adjustHeap(arr, maxIndex, size)
}
return arr
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
计数排序
桶排序
基数排序
二分查找
二分查找可以解决已排序数组的查找问题,即只要数组中包含T(要查找的值),那么通过不断的缩小包含T的数据范围,就可以最终要找到的数 (1) 一开始,数据范围覆盖整个数组。 (2) 将数组的中间项与T进行比较,如果T比数组的中间项小,则到数组的前半部分继续查找,反之,则到数组的后半部分继续查找。 (3) 就这样,每次查找都可以排除一半元素,相当于范围缩小一半。这样反复比较,反复缩小范围,最终会在数组中找到T 代码实现:
const arr = [1, 3, 5, 7, 9, 10];
const target = 7;
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const midIndex = Math.floor((left + right) / 2);
if (arr[midIndex] > target) {
right = midIndex - 1;
} else if (arr[midIndex] < target) {
left = midIndex + 1;
} else {
return midIndex;
}
}
return -1;
}
const index = binarySearch(arr, target);
console.log(index); // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
二叉树
遍历节点
前序遍历
- 根节点
- 访问左子节点,回到 1
- 访问右子节点,回到 1
中序遍历
- 先访问到最左的子节点
- 访问该节点的父节点
- 访问该父节点的右子节点,回到 1
后序遍历
- 先访问到最左的子节点
- 访问相邻的右节点
- 访问父节点,回到 1